package cn.workreport.util;

import cn.workreport.config.conf.TokenConfiguration;
import cn.workreport.constants.Constant;
import cn.workreport.modules.common.entity.TokenParseEntity;
import cn.workreport.modules.common.entity.UserCacheEntity;
import cn.workreport.modules.common.exception.ServiceException;
import cn.workreport.modules.role.entity.Role;
import cn.workreport.modules.users.entity.UserEntity;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@Component
public  class TokenUtil {

    private static final String USER_ID_KEY_OF_TOKEN = "userId";
    private static final String START_TIME_KEY_OF_TOKEN = "startTime";

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    TokenConfiguration tokenConfiguration;

    /**
     * 生成加密 token
     */
    public String createToken(UserEntity user){
        return createToken(user, null);
    }

    public String createToken(UserEntity user, Role userRole) {
        if (ObjectUtils.isEmpty(user)) {
            throw new ServiceException("参数 user 不能为空");
        }
        // 这个是放到负载payLoad 里面,魔法值可以使用常量类进行封装
        String token = JWT
                .create()
                .withClaim(TokenUtil.USER_ID_KEY_OF_TOKEN, Long.valueOf(user.getId()))
                .withClaim(TokenUtil.START_TIME_KEY_OF_TOKEN, System.currentTimeMillis())
                .sign(Algorithm.HMAC256(tokenConfiguration.getPrivateKey()));
        // 将 token 放入 用户信息里
        user.setToken(token);
        UserCacheEntity userCacheEntity = new UserCacheEntity();
        userCacheEntity.setToken(token);
        userCacheEntity.setUser(user);
        userCacheEntity.setUserRole(userRole);
        // 放入 redis
        redisUtil.set(getTokenKey(user.getId()), userCacheEntity, tokenConfiguration.getOldToken(), TimeUnit.MINUTES);
        return token;
    }

    /**
     * 刷新令牌有效期，实际上是生成了一条新的 token，同时 Redis 的缓存也会更新成新的 token
     */
    public String refreshToken (Integer userId) {
        if (ObjectUtils.isEmpty(userId)) {
            throw new ServiceException("refreshToken 参数 token 不能为空");
        }
        UserCacheEntity userCacheEntity = redisUtil.get(getTokenKey(userId));
        // 删除旧的 Redis 缓存
//        redisUtil.delete(getTokenKey(userId));
        if (ObjectUtils.isEmpty(userCacheEntity)) {
            throw new ServiceException("该 token 已过期");
        }
        String newToken = createToken(userCacheEntity.getUser(), userCacheEntity.getUserRole());
        return newToken;
    }

    /**
     * 检查 token 是否存在
     * @return 0 : token 不存在; 1 : 通过; 2 : token 存在，但是已经被更新
     */
    public int checkToken(String token) {
        if (ObjectUtils.isEmpty(token)) {
            throw new ServiceException("checkToken 参数 token 不能为空");
        }
        TokenParseEntity tokenParseEntity = null;
        try {
            tokenParseEntity = parseToken(token);
        } catch (Exception e) {
            return 0;
        }
        return checkToken(token, tokenParseEntity.getUserId());
    }
    /**
     * 检查 token 是否存在
     * @return 0 : token 不存在; 1 : 通过; 2 : token 存在，但是已经被更新
     */
    public int checkToken(String token, Integer userId) {
        if (ObjectUtils.isEmpty(token)) {
            throw new ServiceException("checkToken 参数 token 不能为空");
        }
        if (ObjectUtils.isEmpty(userId)) {
            throw new ServiceException("checkToken 参数 userId 不能为空");
        }
        try {
            if (redisUtil.exists(getTokenKey(userId))) {
                UserCacheEntity userCacheEntity = redisUtil.get(getTokenKey(userId));
                return token.equals(userCacheEntity.getToken()) ? 1 : 2;
            } else {
                return 0;
            }
        } catch (Exception e) {
            return 0;
        }
    }

    /**
     * 解析 token
     */
    public TokenParseEntity parseToken(String token) {
        TokenParseEntity tokenParseEntity = new TokenParseEntity();
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(tokenConfiguration.getPrivateKey())).build().verify(token);
        Claim userId = decodedJWT.getClaim(TokenUtil.USER_ID_KEY_OF_TOKEN);
        Claim timeStamp = decodedJWT.getClaim(TokenUtil.START_TIME_KEY_OF_TOKEN);
        tokenParseEntity.setUserId(userId.asInt());
        tokenParseEntity.setStartTime(timeStamp.asLong());
        System.out.println("\t解析token ==>" + tokenParseEntity.toString());
        return tokenParseEntity;
    }

    /**
     * 获取用户信息
     */
    public UserCacheEntity getUserCache(Integer userId) {
        if (ObjectUtils.isEmpty(userId)) {
            throw new ServiceException("getUserCache 参数 userId 不能为空");
        }
        return redisUtil.get(getTokenKey(userId));
    }

    /**
     * 获取 token 过期时间
     * @return 时间(秒) 返回0代表为永久有效 失效时间为负数，说明该主键未设置失效时间（失效时间默认为-1）
     */
    public long getExpireTime(Integer userId) {
        if (ObjectUtils.isEmpty(userId)) {
            throw new ServiceException("getExpireTime 参数 userId 不能为空");
        }
        return redisUtil.getExpire(getTokenKey(userId));
    }

    /**
     * 删除 Redis token
     */
    public void removeToken(Integer userId) {
        redisUtil.delete(getTokenKey(userId));
    }

    private String getTokenKey(Integer userId) {
        if (ObjectUtils.isEmpty(userId)) {
            throw new ServiceException("getTokenKey 参数 userId 不能为空");
        }
        return Constant.USER_TOKEN_REDIS_KEY_PREFIX + userId;
    }
}
